home *** CD-ROM | disk | FTP | other *** search
/ Power DOS 1996 July / Power DOS - July 1996.iso / sound / c_labs / devinfo / fm.exe / FMSAMPLE.C next >
C/C++ Source or Header  |  1996-02-09  |  18KB  |  511 lines

  1. /* -------------------------------------------------------------------------- */
  2. /*                                                                            */
  3. /* (C) Copyright Creative Technology Ltd 1994-1996. All right reserved        */
  4. /*                                                                            */
  5. /* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY      */
  6. /* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE        */
  7. /* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR      */
  8. /* PURPOSE.                                                                   */
  9. /*                                                                            */
  10. /* You have a royalty-free right to use, modify, reproduce and                */
  11. /* distribute the Sample Files (and/or any modified version) in               */
  12. /* any way you find useful, provided that you agree that                      */
  13. /* Creative has no warranty obligations or liability for any Sample Files.    */
  14. /*                                                                            */
  15. /*----------------------------------------------------------------------------*/
  16.   
  17. /*
  18.  *  This Program is written using Borland C++ Ver 3.1.
  19.  *  To Compile : BCC FMSAMPLE.C
  20.  * ---------------------------------------------------------------------
  21.  *
  22.  * This program is not intended to explain all the aspects of FM sound
  23.  * generation on Sound Blaster cards.  The chips are too complicated for
  24.  * that.  This program is just to demonstrate how to generate a tone and
  25.  * control the left and right channels.  For more information on the FM
  26.  * synthesizer chip, contact Yamaha.
  27.  *
  28.  * Here's a brief description of FM:  Each sound is created by two operator
  29.  * cells (called "slots" in the Yamaha documentation), a modulator and a
  30.  * carrier.  When FM synthesis was invented, the output value of the
  31.  * modulator affected the frequency of the carrier.  In the Yamaha chips, the
  32.  * modulator output actually affects the phase of the carrier instead of
  33.  * frequency, but this has a similar  effect.
  34.  *
  35.  * Normally the modulator and carrier would probably be connected in series
  36.  * for complex sounds.  For this program, I wanted a pure sine wave, so I
  37.  * connected them in parallel and turned the modulator output down all the
  38.  * way and used just the carrier.
  39.  *
  40.  * Sound Blaster 1.0 - 2.0 cards have one OPL-2 FM synthesis chip at
  41.  * addresses 2x8 and 2x9 (base + 8 and base + 9).  Sound Blaster Pro version
  42.  * 1 cards (CT-1330) achieve stereo FM with two OPL-2 chips, one for each
  43.  * speaker.  The left channel FM chip is at addresses 2x0 and 2x1.  The right
  44.  * is at 2x2 and 2x3.  Addresses 2x8 and 2x9 address both chips
  45.  * simultaneously, thus maintaining compatibility with the monaural Sound
  46.  * Blaster cards.  The OPL-2 contains 18 operator cells which make up the
  47.  * nine 2-operator channels.  Since the CT-1330 SB Pro has two OPL-2 chips,
  48.  * it is therefore capable of generating 9 voices in each speaker.
  49.  *
  50.  * Sound Blaster Pro version 2 (CT-1600) and Sound Blaster 16 cards have one
  51.  * OPL-3 stereo FM chip at addresses 2x0 - 2x3.  The OPL-3 is separated into
  52.  * two "banks."  Ports 2x0 and 2x1 control bank 0, while 2x2 and 2x3 control
  53.  * bank 1.  Each bank can generate nine 2-operator voices.  However, when the
  54.  * OPL-3 is reset, it switches into OPL-2 mode.  It must be put into OPL-3
  55.  * mode to use the voices in bank 1 or the stereo features.  For stereo
  56.  * control, each channel may be sent to the left, the right, or both
  57.  * speakers, controlled by two bits in registers C0H - C8H.
  58.  *
  59.  * The FM chips are controlled through a set of registers.  The following
  60.  * table shows how operator cells and channels are related, and the register
  61.  * offsets to use.
  62.  *
  63.  * FUNCTION  MODULATOR-  -CARRIER--  MODULATOR-  -CARRIER--  MODULATOR-  -CARRIER--
  64.  * OP CELL    1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18
  65.  * CHANNEL    1   2   3   1   2   3   4   5   6   4   5   6   7   8   9   7   8   9
  66.  * OFFSET    00  01  02  03  04  05  08  09  0A  0B  0C  0D  10  11  12  13  14  15
  67.  *
  68.  * An example will make the use of this table clearer:  suppose you want to
  69.  * set the attenuation of both of the operators of channel 4.  The KSL/TOTAL LEVEL
  70.  * registers (which set the attenuation) are 40H - 55H.  The modulator for
  71.  * channel 4 is op cell 7, and the carrier for channel 4 is op cell 10.  The
  72.  * offsets for the modulator and carrier cells are 08H and 0BH, respectively.
  73.  * Therefore, to set the attenuation of the modulator, you would output a
  74.  * value to register 40H + 08H == 48H, and to set the carrier's attenuation,
  75.  * you would output to register 40H + 0BH == 4BH.
  76.  *
  77.  * In this program, I used just channel 1, so the registers I used were 20H,
  78.  * 40H, 60H, etc., and 23H, 43H, 63H, etc.
  79.  *
  80.  * The frequencies of each channel are controlled with a frequency number and
  81.  * a multiplier.  The modulator and carrier of a channel both get the same
  82.  * frequency number, but they may be given different multipliers.  Frequency
  83.  * numbers are programmed in registers A0H - A8H (low 8 bits) and B0H - B8H
  84.  * (high 2 bits).  Those registers control entire channels (2 operators), not
  85.  * individual operator cells.  To turn a note on, the key-on bit in the
  86.  * appropriate channel register is set.  Since these registers deal with
  87.  * channels, you do not use the offsets listed in the table above.  Instead,
  88.  * add (channel-1) to A0H or B0H.  For example, to turn channel 1 on,
  89.  * program the frequency number in registers A0H and B0H, and set the key-on
  90.  * bit to 1 in register B0H.  For channel 3, use registers A2H and B2H.
  91.  *
  92.  * Bits 2 - 4 in registers B0H - B8H are the block (octave) number for the
  93.  * channel.
  94.  *
  95.  * Multipliers for each operator cell are programmed through registers 20H -
  96.  * 35H.  The table below shows what multiple number to program into the
  97.  * register to get the desired multiplier.  The multiple number goes into
  98.  * bits 0 - 3 in the register.  Note that it's a bit strange at the end.
  99.  *
  100.  *   multiple number     multiplier        multiple number     multiplier
  101.  *          0                1/2                   8               8
  102.  *          1                 1                    9               9
  103.  *          2                 2                    10              10
  104.  *          3                 3                    11              10
  105.  *          4                 4                    12              12
  106.  *          5                 5                    13              12
  107.  *          6                 6                    14              15
  108.  *          7                 7                    15              15
  109.  *
  110.  * This equation shows how to calculate the required frequency number (to
  111.  * program into registers A0H - A8H and B0H - B8H) to get the desired
  112.  * frequency:
  113.  *                fn=(long)f * 1048576 / b / m /50000L
  114.  * where f is the frequency in Hz,
  115.  *       b is the block (octave) number between 0 and 7 inclusive, and
  116.  *       m is the multiple number between 0 and 15 inclusive.
  117.  *
  118.  */
  119.  
  120.  
  121. #define STEREO         // Define this for SBPro CT-1330 or later card.
  122. #define OPL3           // Also define this for SBPro CT-1600 or later card.
  123.  
  124.  
  125. #include <stdio.h>
  126. #include <stdlib.h>
  127. #include <conio.h>
  128. #include <ctype.h>
  129. #include <dos.h>
  130.  
  131. #define KEYON     0x20     // key-on bit in regs b0 - b8
  132.  
  133.  /* These are offsets from the base I/O address. */
  134. #define FM       8        // SB (mono) ports (e.g. 228H and 229H)
  135. #define PROFM1   0        // On CT-1330, this is left OPL-2.  On CT-1600 and
  136.                           // later cards, it's OPL-3 bank 0.
  137. #define PROFM2   2        // On CT-1330, this is right OPL-2.  On CT-1600 and
  138.                           // later cards, it's OPL-3 bank 1.
  139.  
  140.  
  141.  
  142. #ifdef OPL3
  143.   #define LEFT     0x10
  144.   #define RIGHT       0x20
  145. #endif
  146.  
  147.  
  148. unsigned IOport;        // Sound Blaster port address
  149.  
  150.  
  151.  
  152. void mydelay(unsigned long clocks)
  153. /*
  154.  * "clocks" is clock pulses (at 1.193180 MHz) to elapse, but remember that
  155.  * normally the system timer runs in mode 3, in which it counts down by twos,
  156.  * so delay3(1193180) will only delay half a second.
  157.  *
  158.  *   clocks = time * 2386360
  159.  *
  160.  *     time = clocks / 2386360
  161.  */
  162. {
  163.    unsigned long elapsed=0;
  164.    unsigned int last,next,ncopy,diff;
  165.  
  166.    /* Read the counter value. */
  167.    outp(0x43,0);                              /* want to read timer 0 */
  168.    last=inp(0x40);                            /* low byte */
  169.    last=~((inp(0x40)<< 8) + last);            /* high byte */
  170.  
  171.    do {
  172.       /* Read the counter value. */
  173.       outp(0x43,0);                           /* want to read timer 0 */
  174.       next=inp(0x40);                         /* low byte */
  175.       ncopy=next=~((inp(0x40)<< 8) + next);   /* high byte */
  176.  
  177.       next-=last;      /* this is now number of elapsed clock pulses since last read */
  178.  
  179.       elapsed += next; /* add to total elapsed clock pulses */
  180.       last=ncopy;
  181.    } while (elapsed<clocks);
  182. }
  183.  
  184.  
  185.  
  186. int base16(char **str, unsigned *val)
  187. /* Takes a double pointer to a string, interprets the characters as a
  188.  * base-16 number, and advances the pointer.
  189.  * Returns 0 if successful, 1 if not.
  190.  */
  191. {
  192.    char c;
  193.    int digit;
  194.    *val = 0;
  195.  
  196.    while ( **str != ' ') {
  197.       c = toupper(**str);
  198.       if (c >= '0' && c <= '9')
  199.          digit = c - '0';
  200.       else if (c >= 'A' && c <= 'F')
  201.          digit = c - 'A'  + 10;
  202.       else
  203.          return 1;          // error in string
  204.  
  205.       *val = *val * 16 + digit;
  206.       (*str)++;
  207.    }
  208.    return 0;
  209. }
  210.  
  211.  
  212.  
  213. int base10(char **str, unsigned *val)
  214. /* Takes a double pointer to a string, interprets the characters as a
  215.  * base-10 number, and advances the pointer.
  216.  * Returns 0 if successful, 1 if not.
  217.  */
  218. {
  219.    char c;
  220.    int digit;
  221.    *val = 0;
  222.  
  223.    while ( **str != ' ') {
  224.       c = toupper(**str);
  225.       if (c >= '0' && c <= '9')
  226.          digit = c - '0';
  227.       else
  228.          return 1;          // error in string
  229.  
  230.       *val = *val * 10 + digit;
  231.       (*str)++;
  232.    }
  233.    return 0;
  234. }
  235.  
  236.  
  237.  
  238. unsigned ReadBlasterEnv(unsigned *port, unsigned *irq, unsigned *dma8,
  239.  unsigned *dma16)
  240. /* Gets the Blaster environment statement and stores the values in the
  241.  * variables whose addresses were passed to it.
  242.  * Returns:
  243.  *   0  if successful
  244.  *   1  if there was an error reading the port address.
  245.  *   2  if there was an error reading the IRQ number.
  246.  *   3  if there was an error reading the 8-bit DMA channel.
  247.  *   4  if there was an error reading the 16-bit DMA channel.
  248.  */
  249. {
  250.    char     *env;
  251.    unsigned val;
  252.    int      digit;
  253.  
  254.    env = getenv("BLASTER");
  255.  
  256.    while (*env) {
  257.       switch(toupper( *(env++) )) {
  258.          case 'A':
  259.             if (base16(&env, port))     // interpret port value as hex
  260.                return 1;                // error
  261.             break;
  262.          case 'I':
  263.             if (base10(&env, irq))      // interpret IRQ as decimal
  264.                return 2;                // error
  265.             break;
  266.          case 'D':
  267.             if (base10(&env, dma8))     // 8-bit DMA channel is decimal
  268.                return 3;
  269.             break;
  270.          case 'H':
  271.             if (base10(&env, dma16))    // 16-bit DMA channel is decimal
  272.                return 4;
  273.             break;
  274.          default:
  275.             break;
  276.       }
  277.    }
  278.  
  279.    return 0;
  280. }
  281.  
  282.  
  283.  
  284. void FMoutput(unsigned port, int reg, int val)
  285. /* This outputs a value to a specified FM register at a specified FM port. */
  286. {
  287.    outp(port, reg);
  288.    mydelay(8);          /* need to wait 3.3 microsec */
  289.    outp(port+1, val);
  290.    mydelay(55);         /* need to wait 23 microsec */
  291. }
  292.  
  293.  
  294.  
  295. void fm(int reg, int val)
  296. /* This function outputs a value to a specified FM register at the Sound
  297.  * Blaster (mono) port address.
  298.  */
  299. {
  300.    FMoutput(IOport+FM, reg, val);
  301. }
  302.  
  303.  
  304. void Profm1(int reg, int val)
  305. /* This function outputs a value to a specified FM register at the Sound
  306.  * Blaster Pro left FM port address (or OPL-3 bank 0).
  307.  */
  308. {
  309.    FMoutput(IOport+PROFM1, reg, val);
  310. }
  311.  
  312.  
  313. void Profm2(int reg, int val)
  314. /* This function outputs a value to a specified FM register at the Sound
  315.  * Blaster Pro right FM port address (or OPL-3 bank 1).
  316.  */
  317. {
  318.    FMoutput(IOport+PROFM2, reg, val);
  319. }
  320.  
  321.  
  322.  
  323.  
  324. void main(void)
  325. {
  326.    int i,val1,val2;
  327.  
  328.    int block,b,m,f,fn;
  329.  
  330.    unsigned dummy;
  331.  
  332.  
  333.    clrscr();
  334.  
  335.    ReadBlasterEnv(&IOport, &dummy, &dummy, &dummy);
  336.  
  337. #ifdef STEREO
  338.  #ifdef OPL3
  339.    printf("Program compiled for Sound Blaster Pro ver. 2 (CT-1600) and SB16 cards.\n");
  340.  #else
  341.    printf("Program compiled for Sound Blaster Pro ver. 1 (CT-1330) cards.\n");
  342.  #endif
  343. #else
  344.    printf("Program compiled for Sound Blaster 1.0 - 2.0 cards (monaural).\n");
  345. #endif
  346.  
  347.  
  348.    fm(1,0);        /* must initialize this to zero */
  349.  
  350. #ifdef OPL3
  351.    Profm2(5, 1);  /* set to OPL3 mode, necessary for stereo */
  352.    fm(0xC0,LEFT | RIGHT | 1);     /* set both channels, parallel connection */
  353. #else
  354.    fm(0xC0,               1);     /* parallel connection */
  355. #endif
  356.  
  357.    /***************************************
  358.     * Set parameters for the carrier cell *
  359.     ***************************************/
  360.  
  361.    fm(0x23,0x21);  /* no amplitude modulation (D7=0), no vibrato (D6=0),
  362.                     * sustained envelope type (D5=1), KSR=0 (D4=0),
  363.                     * frequency multiplier=1 (D4-D0=1)
  364.                     */
  365.  
  366.    fm(0x43,0x0);   /* no volume decrease with pitch (D7-D6=0),
  367.                     * no attenuation (D5-D0=0)
  368.                     */
  369.  
  370.    fm(0x63,0xff);  /* fast attack (D7-D4=0xF) and decay (D3-D0=0xF) */
  371.    fm(0x83,0x05);  /* high sustain level (D7-D4=0), slow release rate (D3-D0=5) */
  372.  
  373.  
  374.    /*****************************************
  375.     * Set parameters for the modulator cell *
  376.     *****************************************/
  377.  
  378.    fm(0x20,0x20);  /* sustained envelope type, frequency multiplier=0    */
  379.    fm(0x40,0x3f);  /* maximum attenuation, no volume decrease with pitch */
  380.  
  381.    /* Since the modulator signal is attenuated as much as possible, these
  382.     * next two values shouldn't have any effect.
  383.     */
  384.    fm(0x60,0x44);  /* slow attack and decay */
  385.    fm(0x80,0x05);  /* high sustain level, slow release rate */
  386.  
  387.  
  388.    /*************************************************
  389.     * Generate tone from values looked up in table. *
  390.     *************************************************/
  391.  
  392.    printf("440 Hz tone, values looked up in table.\n");
  393.    fm(0xa0,0x41);  /* 440 Hz */
  394.    fm(0xb0,0x32);  /* 440 Hz, block 0, key on */
  395.  
  396.    getche();
  397.  
  398.    fm(0xb0,0x12);  /* key off */
  399.  
  400.  
  401.    /******************************************
  402.     * Generate tone from a calculated value. *
  403.     ******************************************/
  404.  
  405.    printf("440 Hz tone, values calculated.\n");
  406.    block=4;        /* choose block=4 and m=1 */
  407.    m=1;               /* m is the frequency multiple number */
  408.    f=440;          /* want f=440 Hz */
  409.    b= 1 << block;
  410.  
  411.    /* This is the equation to calculate frequency number from frequency. */
  412.  
  413.    fn=(long)f * 1048576 / b / m /50000L;
  414.  
  415.    fm(0x23,0x20 | (m & 0xF));   /* 0x20 sets sustained envelope, low nibble
  416.                                  * is multiple number
  417.                                  */
  418.    fm(0xA0,(fn & 0xFF));
  419.    fm(0xB0,((fn >> 8) & 0x3) + (block << 2) | KEYON);
  420.  
  421.    getche();
  422.  
  423.  
  424.    /*********************************************************
  425.     * Generate a range of octaves by changing block number. *
  426.     *********************************************************/
  427.  
  428.    printf("Range of frequencies created by changing block number.\n");
  429.    for (block=0; block<=7; block++) {
  430.       printf("f=%5ld Hz (press Enter)\n",(long)440*(1 << block)/16);
  431.       fm(0xB0,((fn >> 8) & 0x3) + (block << 2) | KEYON);
  432.       getche();
  433.    }
  434.  
  435.  
  436.    /*****************************************************************
  437.     * Generate a range of frequencies by changing frequency number. *
  438.     *****************************************************************/
  439.  
  440.    printf("Range of frequencies created by changing frequency number.\n");
  441.    block=4;
  442.    for (fn=0; fn<1024; fn++) {
  443.       fm(0xA0,(fn & 0xFF));
  444.       fm(0xB0,((fn >> 8) & 0x3) + (block << 2) | KEYON);
  445.       delay(1);
  446.    }
  447.  
  448.  
  449.    /********************************************************************
  450.     * Single tone again.  Both channels, then if on stereo board,      *
  451.     * play tone in just the left channel, then just the right channel. *
  452.     ********************************************************************/
  453.  
  454.    printf("440 Hz again, both channels.\n");
  455.    block=4;
  456.    fn=577;                /* This number makes 440 Hz when block=4 and m=1 */
  457.    fm(0xA0,(fn & 0xFF));
  458.    fm(0xB0,((fn >> 8) & 0x3) + (block << 2) | KEYON);
  459.  
  460. #ifdef STEREO
  461.  #ifdef OPL3
  462.     /* This left and right channel stuff is the only part of this program
  463.      * that uses OPL3 mode.  Everything else is available on the OPL2.
  464.      */
  465.  
  466.     getche();
  467.     printf("Left channel only\n");
  468.     fm(0xC0,LEFT | 1);      /* set left channel only, parallel connection */
  469.  
  470.     getche();
  471.     printf("Right channel only\n");
  472.     fm(0xC0,RIGHT | 1);     /* set right channel only, parallel connection */
  473.  #else
  474.     getche();
  475.     fm(0xB0,((fn >> 8) & 0x3) + (block << 2));       // key off
  476.  
  477.     printf("Left channel only\n");
  478.     Profm1(0xB0,((fn >> 8) & 0x3) + (block << 2) | KEYON);
  479.  
  480.     getche();
  481.     Profm1(0xB0,((fn >> 8) & 0x3) + (block << 2));   // key off
  482.  
  483.     printf("Right channel only\n");
  484.     Profm2(0xB0,((fn >> 8) & 0x3) + (block << 2) | KEYON);
  485.  
  486.  
  487.  #endif
  488. #endif
  489.  
  490.  
  491.    /*********************************
  492.     * Attenuate the signal by 3 dB. *
  493.     *********************************/
  494.  
  495.    getche();
  496.    fm(0xB0,((fn >> 8) & 0x3) + (block << 2) | KEYON);
  497.    printf("Attenuated by 3 dB.\n");
  498.    fm(0x43,4);     /* attenuate by 3 dB */
  499.    getche();
  500.  
  501.    fm(0xB0,((fn >> 8) & 0x3) + (block << 2));
  502.  
  503. #ifdef OPL3
  504.    /* Set OPL-3 back to OPL-2 mode, because if the next program to run was
  505.     * written for the OPL-2, then it won't set the LEFT and RIGHT bits to
  506.     * one, so no sound will be heard.
  507.     */
  508.    Profm2(5, 0);   /* set back to OPL2 mode */
  509. #endif
  510. }
  511.